/*
 * 文件名： WcPayCmbcHelper.java
 * 
 * 工程名称: bis-bank-wechat
 *
 * Gopay
 *
 * 创建日期： 2017年5月16日
 *
 * Copyright(C) 2017, by www.gopay.com.cn Inc.All rights reserved.
 *
 * 原始作者: 宣广海
 *
 */
package com.gopay.bis.cmbc.wcpay.operation;

import java.io.ByteArrayInputStream;
import java.io.UnsupportedEncodingException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.xml.rpc.ServiceException;

import org.apache.commons.codec.binary.Base64;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.InputSource;

import com.gopay.common.constants.proccode.ProcCodeConstants;
import com.gopay.common.domain.bank.wechat.wcpay.cmbc.TransMsg;
import com.gopay.common.exception.GopayException;
import com.gopay.remote.bank.ICertHelperThirdParty;

import net.sf.json.JSONObject;

/**
 * 
 *
 * @author 宣广海
 *
 * @version
 *
 * @since 2017年5月16日
 */
@SuppressWarnings("all")
public class WcPayCmbcHelper implements ICertHelperThirdParty {
	private static Logger logger = LoggerFactory.getLogger(WcPayCmbcHelper.class);

	public static final String CHARSET = "utf-8";
	public static final String AES_ENCRYPT = "AES/ECB/PKCS5Padding";
	public static final String RSA_ENCRYPT = "RSA/ECB/PKCS1Padding";
	public static final String RSA_SIGN = "SHA1WithRSA";

	@Override
	public String sign(Object requestMsg) throws GopayException {
		TransMsg tranMsg = (TransMsg) requestMsg;
		String privateKeyPath = tranMsg.getPrivateKeyPath();
		try {
			PrivateKey hzfPriKey = CryptoUtil.getRSAPrivateKeyByFileSuffix(privateKeyPath, "pem", null, "RSA");
			byte[] plainBytes = tranMsg.getPlainMessage().getBytes(CHARSET);
			return new String(Base64.encodeBase64(CryptoUtil.digitalSign(plainBytes, hzfPriKey, RSA_SIGN)), CHARSET);
		} catch (ServiceException | UnsupportedEncodingException e) {
			throw new GopayException(ProcCodeConstants.PROC_CODE_100E6029, e.getMessage());
		}
	}

	/**
	 * verify
	 * 
	 * @param obj
	 */
	@Override
	public Object verify(Object obj) throws GopayException {
		TransMsg message = (TransMsg) obj;
		String plainText = message.getPlainMessage();
		String signData = message.getSignMessage();
		String pubKeyPath = message.getPublicKeyPath();
		try {
			PublicKey yhPubKey = CryptoUtil.getRSAPublicKeyByFileSuffix(pubKeyPath, "pem", "RSA");
			byte[] signBytes = Base64.decodeBase64(signData.getBytes(CHARSET));
			return CryptoUtil.verifyDigitalSign(plainText.getBytes(CHARSET), signBytes, yhPubKey, RSA_SIGN);
		} catch (Exception e) {
			throw new GopayException(ProcCodeConstants.PROC_CODE_100E6040, e);
		}
	}

	/**
	 * encryt
	 * 
	 * @param requestMsg
	 *            BaseMessage
	 * @param keyBytes
	 */
	public String encryptData(Object requestMsg, byte[] keyBytes) throws GopayException {
		@SuppressWarnings("rawtypes")
		TransMsg messageScan = (TransMsg) requestMsg;
		try {
			byte[] data = messageScan.getPlainMessage().getBytes(CHARSET);
			return new String(Base64.encodeBase64((CryptoUtil.AESEncrypt(data, keyBytes, "AES", AES_ENCRYPT, null))),
					CHARSET);
		} catch (Exception e) {
			throw new GopayException("cmbc wcpay Message encryption exception ", e);
		}
	}

	/**
	 * rsa encryt
	 * 
	 * @param keyBytes
	 * @param requestMsg
	 */
	public String encrtptKey(byte[] keyBytes, Object requestMsg) throws GopayException {
		TransMsg messageScan = (TransMsg) requestMsg;
		String publiceKeyPath = messageScan.getPublicKeyPath();
		try {
			PublicKey yhPubKey = CryptoUtil.getRSAPublicKeyByFileSuffix(publiceKeyPath, "pem", "RSA");
			return new String(Base64.encodeBase64(CryptoUtil.RSAEncrypt(keyBytes, yhPubKey, 2048, 11, RSA_ENCRYPT)),
					CHARSET);
		} catch (Exception e) {
			throw new GopayException("cmbc wcpay Key encryption exception", e);
		}
	}

	/**
	 * 
	 * 功能描述：解析并验签返回报文
	 *
	 * @param response
	 * @param requestMsg
	 * @return
	 * @throws GopayException
	 * 
	 * @author 宣广海
	 *
	 * @since 2017年5月16日
	 *
	 * @update:[变更日期YYYY-MM-DD][更改人姓名][变更描述]
	 */
	public String buildResponseData(String response, Object requestMsg) throws GopayException {
		TransMsg messageWcpay = (TransMsg) requestMsg;
		JSONObject jsonObject = JSONObject.fromObject(response);
		String resEncryptData = jsonObject.getString("encryptData");
		String resEncryptKey = jsonObject.getString("encryptKey");
		String signData = jsonObject.getString("signData");
		PrivateKey hzfPriKey = null;
		byte[] merchantXmlDataBytes = null;
		String plainText = null;
		try {
			hzfPriKey = CryptoUtil.getRSAPrivateKeyByFileSuffix(messageWcpay.getPrivateKeyPath(), "pem", null, "RSA");
			byte[] decodeBase64KeyBytes = Base64.decodeBase64(resEncryptKey.getBytes(CHARSET));
			byte[] merchantAESKeyBytes = CryptoUtil.RSADecrypt(decodeBase64KeyBytes, hzfPriKey, 2048, 11, RSA_ENCRYPT);
			byte[] decodeBase64DataBytes = Base64.decodeBase64(resEncryptData.getBytes(CHARSET));
			merchantXmlDataBytes = CryptoUtil.AESDecrypt(decodeBase64DataBytes, merchantAESKeyBytes, "AES", AES_ENCRYPT,
					null);
			plainText = new String(merchantXmlDataBytes, CHARSET);
			logger.info("cmbc wcpay response plainText : " + plainText);
			/**
			 * 验签
			 */
			TransMsg message = new TransMsg();
			message.setPlainMessage(plainText);
			message.setSignMessage(signData);
			message.setPublicKeyPath(messageWcpay.getPublicKeyPath());
			if ((boolean) verify(message)) {
				return plainText;
			} else {
				throw new GopayException(ProcCodeConstants.PROC_CODE_100E7026);
			}
		} catch (Exception e) {
			logger.error("buildResponseData error :" + e.getMessage());
			throw new GopayException(ProcCodeConstants.PROC_CODE_100F1002);
		}
	}

	public Map<String, String> generateResMsgtoMap(String plainText) throws DocumentException {
		SAXReader reader = new SAXReader(false);
		InputSource source = new InputSource(new ByteArrayInputStream(plainText.getBytes()));
		source.setEncoding(CHARSET);
		Document doc = reader.read(source);
		Map<String, String> params = new HashMap<String, String>();
		params = parseXML(doc.getRootElement(), params);
		return params;
	}

	private Map<String, String> parseXML(Element ele, Map<String, String> map) {
		for (Iterator<?> i = ele.elementIterator(); i.hasNext();) {
			Element node = (Element) i.next();
			logger.info("parseXML node name:" + node.getName());
			if (node.attributes() != null && node.attributes().size() > 0) {
				for (Iterator<?> j = node.attributeIterator(); j.hasNext();) {
					Attribute item = (Attribute) j.next();
					map.put(item.getName(), item.getValue());
				}
			}
			if (node.getText().length() > 0) {
				map.put(node.getName(), node.getText());
			}
			if (node.elementIterator().hasNext()) {
				parseXML(node, map);
			}
		}
		return map;
	}

}
